### Eclipse Workspace Patch 1.0
#P OpenConcerto
Index: src/org/openconcerto/erp/core/sales/pos/ui/CaisseFrame.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/CaisseFrame.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/CaisseFrame.java	(working copy)
@@ -37,6 +37,7 @@
 import org.openconcerto.utils.ExceptionHandler;
 import org.openconcerto.utils.cc.ExnTransformer;
 
+import java.awt.Component;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Frame;
@@ -122,6 +123,9 @@
                 c.load();
 
             }
+            
+            // TODO Ajouter la gestion des arguments en ligne de commande
+            
             final ComptaPropsConfiguration conf = POSConfiguration.getInstance().createConnexion();
 
             final int userID = POSConfiguration.getInstance().getUserID();
@@ -189,12 +193,17 @@
                         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
                         f.pack();
-                        f.setLocation(0, 0);
+                        if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) {
+                        	f.setLocation(0, 24);
+                        } else {
+                        	f.setLocation(0, 0);
+                        }
                         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
                         if (POSConfiguration.getInstance().getScreenWidth() > 0 && POSConfiguration.getInstance().getScreenHeight() > 0) {
-                            f.setSize(new Dimension(POSConfiguration.getInstance().getScreenWidth(), POSConfiguration.getInstance().getScreenHeight()));
+                            f.setSize(new Dimension(POSConfiguration.getInstance().getScreenWidth() - f.getX(), 
+                            		POSConfiguration.getInstance().getScreenHeight() - f.getY()));
                         } else {
-                            f.setSize(screenSize);
+                            f.setSize(new Dimension(screenSize.getSize().width - f.getX() , screenSize.getSize().height - f.getY()));//screenSize);
                         }
                         System.out.println("Affichage de l'interface");
                         f.setVisible(true);
@@ -398,30 +407,34 @@
     }
 
     public void showPriceEditor(Article article, CaisseControler caisseControler) {
-        getControler().disableBarcodeReader();
-        System.out.println("CaisseFrame.showPriceEditor()");
-        this.invalidate();
-        final PriceEditorPanel panel = new PriceEditorPanel(this, article);
-
-        final POSGlassPane glassPane2 = new POSGlassPane(panel, (getWidth() - panel.getPreferredSize().width) / 2, 100) {
-            @Override
-            public void mousePressed(MouseEvent e) {
-                Point containerPoint = SwingUtilities.convertPoint(this, e.getPoint(), panel);
-                if (containerPoint.x < 0 || containerPoint.x > panel.getWidth() || containerPoint.y < 0 || containerPoint.y > panel.getHeight()) {
-                    setGlassPane(new JPanel());
-                    getGlassPane().setVisible(false);
-                    getControler().enableBarcodeReader();
-
-                }
-                super.mousePressed(e);
-            }
-
-        };
-        this.setGlassPane(glassPane2);
-        this.getGlassPane().setVisible(true);
-        this.validate();
-        this.repaint();
-
+    	Component c = this.getGlassPane();
+    	if ((c instanceof POSGlassPane) && (c.isVisible())) { // If the POSGlassPane is already on screen do not display it twice
+            System.err.println("CaisseFrame PriceEditor already on screen");
+    	} else {
+	        getControler().disableBarcodeReader();
+	        System.out.println("CaisseFrame.showPriceEditor()");
+	        this.invalidate();
+	        final PriceEditorPanel panel = new PriceEditorPanel(this, article);
+	
+	        final POSGlassPane glassPane2 = new POSGlassPane(panel, (getWidth() - panel.getPreferredSize().width) / 2, 100) {
+	            @Override
+	            public void mousePressed(MouseEvent e) {
+	                Point containerPoint = SwingUtilities.convertPoint(this, e.getPoint(), panel);
+	                if (containerPoint.x < 0 || containerPoint.x > panel.getWidth() || containerPoint.y < 0 || containerPoint.y > panel.getHeight()) {
+	                    setGlassPane(new JPanel());
+	                    getGlassPane().setVisible(false);
+	                    getControler().enableBarcodeReader();
+	
+	                }
+	                super.mousePressed(e);
+	            }
+	
+	        };
+	        this.setGlassPane(glassPane2);
+	        this.getGlassPane().setVisible(true);
+	        this.validate();
+	        this.repaint();
+    	}
     }
 
     public void showCaisse() {
Index: src/org/openconcerto/erp/core/sales/pos/ui/TicketCellRenderer.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/TicketCellRenderer.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/TicketCellRenderer.java	(working copy)
@@ -114,7 +114,7 @@
     }
 
     public static String centsToString(int cents) {
-        final int c = cents % 100;
+        final int c = Math.abs(cents) % 100; // Handle negative value i.e. -8.9 must return "-8.90" instead of "-8.-90"
         String sc = String.valueOf(c);
         if (c < 10) {
             sc = "0" + sc;
Index: src/org/openconcerto/erp/core/sales/pos/ui/CaissePanel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/CaissePanel.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/CaissePanel.java	(working copy)
@@ -61,6 +61,7 @@
     private StatusBar st;
     private ArticleSelectorPanel articleSelectorPanel;
     private ArticleSearchPanel articleSearchPanel;
+    private TicketPanel t;
 
     private JPanel articleSelector;
 
@@ -83,7 +84,7 @@
 
         this.add(this.st, c);
 
-        TicketPanel t = new TicketPanel(this.controler);
+        t = new TicketPanel(this.controler);
         // fillExampleArticle();
         loadArticles(dir);
         c.gridx = 0;
@@ -295,7 +296,7 @@
         }
         final int total = this.controler.getTotal();
         euros = CaisseControler.getEuros(total) + ".";
-        cents = CaisseControler.getCents(total);
+        cents = CaisseControler.getCents(Math.abs(total)); // Handle negative value i.e. -8.9 must return "-8.90" instead of "-8.-90"
         r = g.getFontMetrics().getStringBounds(euros, g);
         x = x - (int) r.getWidth();
         g.drawString(euros, x, y);
@@ -324,7 +325,7 @@
         g.setColor(Color.GRAY);
         g.drawString("Payé", x - (int) r2.getWidth() - (int) r.getWidth() - 10, y);
         // A rendre
-        final boolean minimalHeight = this.getHeight() < 750;
+        final boolean minimalHeight = ((this.getHeight() - t.getHeight()) < (y +40));//this.getHeight() < 750;  // Minimum height adjusted for 10" screens
         if (!minimalHeight) {
             y += 40;
             x = 300;
Index: src/org/openconcerto/erp/core/sales/pos/ui/ArticleSearchPanel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/ArticleSearchPanel.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/ArticleSearchPanel.java	(working copy)
@@ -195,14 +195,21 @@
 
     @Override
     public void valueChanged(ListSelectionEvent e) {
-        Object sel = list.getSelectedValue();
-        if (sel != null && !e.getValueIsAdjusting()) {
-            if (sel instanceof Article) {
-                Article article = (Article) sel;
-                controler.setArticleSelected(article);
-                controler.addArticle(article);
-            }
-        }
+    	Object sel = list.getSelectedValue();
+    	if (sel != null && !e.getValueIsAdjusting()) {
+    		if (sel instanceof Article) {
+    			Article article = (Article) sel;
+    			boolean refresh = true;
+    			final Article articleSelected = this.controler.getArticleSelected();
+    			if (articleSelected != null) {
+    				refresh = !articleSelected.equals(article);
+    			}
+    			if (refresh) {
+    				controler.setArticleSelected(article);
+    				controler.addArticle(article);
+    			}
+    		}
+    	}
     }
 
     public void paintCategorie(final Font f, Font f2, Graphics g, Categorie c, boolean isSelected, int posY, int cellWidth, int cellHeight) {
Index: src/org/openconcerto/erp/core/sales/pos/ui/CaisseControler.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/CaisseControler.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/CaisseControler.java	(working copy)
@@ -13,12 +13,14 @@
  
  package org.openconcerto.erp.core.sales.pos.ui;
 
+import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
 import org.openconcerto.erp.core.sales.pos.POSConfiguration;
 import org.openconcerto.erp.core.sales.pos.io.BarcodeReader;
 import org.openconcerto.erp.core.sales.pos.io.ConcertProtocol;
 import org.openconcerto.erp.core.sales.pos.io.ESCSerialDisplay;
 import org.openconcerto.erp.core.sales.pos.io.TicketPrinter;
 import org.openconcerto.erp.core.sales.pos.model.Article;
+import org.openconcerto.erp.core.sales.pos.model.Categorie;
 import org.openconcerto.erp.core.sales.pos.model.Client;
 import org.openconcerto.erp.core.sales.pos.model.Paiement;
 import org.openconcerto.erp.core.sales.pos.model.ReceiptCode;
@@ -29,6 +31,7 @@
 import org.openconcerto.erp.core.sales.pos.model.Ticket;
 import org.openconcerto.erp.preferences.TemplateNXProps;
 import org.openconcerto.sql.element.SQLElementDirectory;
+import org.openconcerto.utils.DecimalUtils;
 import org.openconcerto.utils.FileUtils;
 import org.openconcerto.utils.Pair;
 import org.openconcerto.utils.StringUtils;
@@ -43,6 +46,8 @@
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -50,11 +55,12 @@
 import java.util.TimerTask;
 
 import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
 
 import org.jdom2.JDOMException;
 
 public class CaisseControler implements BarcodeListener {
-
+	
     private Article articleSelected;
     private Paiement paiementSelected;
     private Ticket t;
@@ -114,7 +120,7 @@
     }
 
     void setArticleSelected(Article a) {
-        if (a != articleSelected) {
+    	if (a != articleSelected) {
             this.articleSelected = a;
             this.paiementSelected = null;
             fire();
@@ -159,12 +165,50 @@
 
     // Articles
     void addArticle(Article a) {
-        this.t.addArticle(a);
-        fire();
-        String price = TicketCellRenderer.centsToString(a.getPriceWithTax().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue());
-        this.setLCD(a.getName(), price, 0);
-        this.setLCDDefaultDisplay(2);
-    }
+    	BigDecimal zero = new BigDecimal(0);
+    	if ((zero.compareTo(a.getPriceWithTax()) == 0) 
+    		&& (zero.compareTo(a.getPriceWithoutTax()) == 0)
+    		&& (a.getId() > 0)) { // The selected article has no price the user must be allowed to set a price 
+    		int newID = Article.getNewTempID();
+			int idTaxe = a.getIdTaxe();
+			float tva = TaxeCache.getCache().getTauxFromId(idTaxe);
+			BigDecimal ht = a.getPriceWithoutTax().setScale(4);
+
+			Article articleManuelPiece = new Article(a.getCategorie(), a.getName(), newID); // Duplicate the article with a new ID
+			articleManuelPiece.setPriceWithoutTax(a.getPriceWithoutTax());
+			articleManuelPiece.setPriceWithTax(ht.multiply(new BigDecimal(tva).movePointLeft(2).add(BigDecimal.ONE), DecimalUtils.HIGH_PRECISION));//new BigDecimal(1.0 + (tva / 100.0D)));
+			articleManuelPiece.setIdTaxe(idTaxe);
+			this.t.addArticle(articleManuelPiece);
+			
+			Runnable code = new Runnable() {
+				public void run() {
+					try {
+						CaisseControler caisseControl = caisseFrame.getControler();
+						caisseFrame.showPriceEditor(articleManuelPiece, caisseControl);
+
+						caisseControl.fire();
+						caisseControl.setArticleSelected(null);
+						String price = TicketCellRenderer.centsToString(articleManuelPiece.getPriceWithTax().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue());
+						caisseControl.setLCD(articleManuelPiece.getName(), price, 0);
+						caisseControl.setLCDDefaultDisplay(2);
+					} catch (Exception e) {
+					}
+				}
+			};
+
+			if (SwingUtilities.isEventDispatchThread()) {
+				code.run();
+			} else {
+				SwingUtilities.invokeLater(code);
+			}
+		} else {
+			this.t.addArticle(a);
+			fire();
+			String price = TicketCellRenderer.centsToString(a.getPriceWithTax().movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue());
+			this.setLCD(a.getName(), price, 0);
+			this.setLCDDefaultDisplay(2);
+		}
+   	}
 
     void incrementArticle(Article a) {
         this.t.incrementArticle(a);
@@ -354,6 +398,7 @@
         this.setPaiementSelected(null);
         this.setArticleSelected(null);
         client = Client.NONE;
+        Categorie.removeAllTemporaryItems(); // Clear all temporary article from the previous ticket
         return res;
     }
 
@@ -453,7 +498,16 @@
             }
         }
         if (existingArticle != null) {
-            existingArticle.updatePriceWithoutTax(ht);
+            existingArticle.updatePriceWithoutTax(ht); // Update the ticket's article price
+            if (existingArticle.getId() < 0) { // Temporary article -> update the price in the Article selector list
+            	Article a = getArticleSelected();
+            	try {
+            		if (a.getId() == existingArticle.getId()) {
+            			a.setPriceWithoutTax(existingArticle.getPriceWithoutTax());            	
+            			a.setPriceWithTax(existingArticle.getPriceWithTax());
+            		}
+            	} catch ( Exception e ) {}
+            }
             fire();
         }
     }
Index: src/org/openconcerto/erp/core/sales/pos/ui/TextAreaTicketPanel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/TextAreaTicketPanel.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/TextAreaTicketPanel.java	(working copy)
@@ -116,7 +116,7 @@
             a.setPriceWithoutTax(ht);
             int idTaxe = row2.getInt("ID_TAXE");
             float tva = TaxeCache.getCache().getTauxFromId(idTaxe);
-            a.setPriceWithTax(ht.multiply(new BigDecimal(1.0 + (tva / 100.0D)), DecimalUtils.HIGH_PRECISION));
+            a.setPriceWithTax(ht.multiply(new BigDecimal(tva).movePointLeft(2).add(BigDecimal.ONE), DecimalUtils.HIGH_PRECISION)); // use conventional expression instead of a custom one : new BigDecimal(1.0 + (tva / 100.0D)), DecimalUtils.HIGH_PRECISION));
             a.setIdTaxe(idTaxe);
             t.addArticle(a);
             t.setArticleCount(a, row2.getInt("QTE"));
Index: src/org/openconcerto/erp/core/sales/pos/model/Categorie.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/model/Categorie.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/model/Categorie.java	(working copy)
@@ -74,6 +74,23 @@
         }
     }
 
+
+    public void removeTemporaryItems() {
+    	List<Article> tempArticles = new ArrayList<>();
+    	for (Article a : this.articles) { // Build the list of temporary articles
+            if (a.getId() < 0) {
+            	tempArticles.add(a);
+            }
+        }
+    	this.articles.removeAll(tempArticles); // Remove all temporary article at once
+    }
+    
+    public static void removeAllTemporaryItems() {
+        for (Categorie c : allCategories) {
+            c.removeTemporaryItems();
+        }
+    }
+    
     public static List<Categorie> getTopLevelCategories() {
         return topLevelCategories;
     }
Index: src/org/openconcerto/erp/core/sales/pos/ui/ArticleModel.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/ArticleModel.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/ArticleModel.java	(working copy)
@@ -51,12 +51,21 @@
     }
 
     public void setCategorie(Categorie c) {
-        this.categorie = c;
-        this.items.clear();
-        if (c != null) {
-            this.items.addAll(c.getArticles());
-        }
-        fire();
+    	boolean shouldUpdate = true;
+    	
+    	if (this.categorie == null) {
+    		shouldUpdate = true;
+    	} else {
+    		shouldUpdate = !this.categorie.equals(c);
+    	}
+		if (shouldUpdate) { // Update only if needed to avoid unnecessary events
+			this.categorie = c;
+			this.items.clear();
+			if (c != null) {
+				this.items.addAll(c.getArticles()); 
+			}
+			fire();
+		}
     }
 
     private void fire() {
Index: src/org/openconcerto/erp/core/sales/pos/model/Ticket.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/model/Ticket.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/model/Ticket.java	(working copy)
@@ -357,7 +357,7 @@
         // Date
         prt.addToBuffer("");
         final SimpleDateFormat df = new SimpleDateFormat("EEEE d MMMM yyyy à HH:mm", Locale.FRENCH);
-        prt.addToBuffer(DefaultTicketPrinter.formatRight(maxWidth, "Le " + df.format(getCreationDate())));
+        prt.addToBuffer(DefaultTicketPrinter.formatCenter(maxWidth, "Le " + df.format(getCreationDate())));
         prt.addToBuffer("");
         List<Pair<Article, Integer>> itemsToPrint = new ArrayList<>(this.items);
         Collections.sort(itemsToPrint, new Comparator<Pair<Article, Integer>>() {
@@ -385,7 +385,7 @@
         Categorie currentCategorie = null;
         for (final Pair<Article, Integer> item : this.items) {
             final Article article = item.getFirst();
-            if (currentCategorie == null || !currentCategorie.equals(article.getCategorie())) {
+            if (currentCategorie == null || !currentCategorie.getName().equals(article.getCategorie().getName())) {
                 // Print category name, except for unknown
                 currentCategorie = article.getCategorie();
                 if (!currentCategorie.isUnknown()) {
@@ -395,22 +395,23 @@
             final Integer nb = item.getSecond();
             final Float tauxFromId = TaxeCache.getCache().getTauxFromId(article.getIdTaxe());
             final BigDecimal tauxTVA = new BigDecimal(tauxFromId).movePointLeft(2).add(BigDecimal.ONE);
+            final BigDecimal unitPrice = article.getPriceWithoutTax().multiply(tauxTVA, DecimalUtils.HIGH_PRECISION);
             final BigDecimal multiply = article.getPriceWithoutTax().multiply(new BigDecimal(nb), DecimalUtils.HIGH_PRECISION).multiply(tauxTVA, DecimalUtils.HIGH_PRECISION);
 
+            final String qtyString = DefaultTicketPrinter.formatRight(MAX_QTE_WIDTH, String.valueOf(nb));
+            final String priceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(multiply.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
+            final String unitPriceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(unitPrice.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
+
             if (article.getCode() != null && !article.getCode().isEmpty()) {
                 // 2 lines
-                final String qtyString = DefaultTicketPrinter.formatRight(MAX_QTE_WIDTH, String.valueOf(nb));
-                final String codeString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH, article.getCode());
-                final String priceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(multiply.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
-                prt.addToBuffer(qtyString + " " + codeString + " " + priceString);
+                final String codeString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH -1 - MAX_PRICE_WIDTH, article.getCode());
+                prt.addToBuffer(qtyString + " " + codeString + " " + unitPriceString + " " + priceString);
                 final String nameString = DefaultTicketPrinter.formatLeft(maxWidth - MAX_QTE_WIDTH - 1, article.getName());
                 prt.addToBuffer("      " + nameString);
             } else {
                 // 1 line
-                final String qtyString = DefaultTicketPrinter.formatRight(MAX_QTE_WIDTH, String.valueOf(nb));
-                final String nameString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH, article.getName());
-                final String priceString = DefaultTicketPrinter.formatRight(MAX_PRICE_WIDTH, TicketCellRenderer.centsToString(multiply.movePointRight(2).setScale(0, RoundingMode.HALF_UP).intValue()));
-                prt.addToBuffer(qtyString + " " + nameString + " " + priceString);
+                final String nameString = DefaultTicketPrinter.formatLeft(maxWidth - 2 - MAX_PRICE_WIDTH - MAX_QTE_WIDTH -1 - MAX_PRICE_WIDTH, article.getName());
+                prt.addToBuffer(qtyString + " " + nameString + " " + unitPriceString + " " + priceString);
             }
 
         }
@@ -522,8 +523,8 @@
                 break;
             }
         }
-        if (!alreadyExist) {
-            final Pair<Article, Integer> line = new Pair<Article, Integer>(new Article(a), 1);
+        if ((!alreadyExist) && (a != null)) {
+            final Pair<Article, Integer> line = new Pair<Article, Integer>((a.getId() < 0) ? a : new Article(a), 1); // Temporary items (ID < 0) are created on the fly and must not be duplicated into the ticket so his price can be modified
             this.items.add(line);
         }
 
@@ -595,7 +596,7 @@
     }
 
     public void setArticleCount(final Article article, final int count) {
-        if (count <= 0) {
+    	if (count == 0) { // Allow negative items count to allow customers to exchange products // if (count <= 0) {
             this.clearArticle(article);
             return;
         }
Index: src/org/openconcerto/erp/core/sales/pos/ui/ArticleSelector.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/ArticleSelector.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/ArticleSelector.java	(working copy)
@@ -114,9 +114,9 @@
                 if (nb > 1) {
                     Object sel = list.getSelectedValue();
                     if (sel != null) {
-                        Article article = (Article) sel;
-                        controller.incrementArticle(article);
-                        controller.setArticleSelected(article);
+						Article article = (Article) sel;
+						controller.incrementArticle(article);
+						controller.setArticleSelected(article);
                     }
                 }
             }
@@ -143,9 +143,16 @@
         Object sel = list.getSelectedValue();
         if (sel != null && !e.getValueIsAdjusting()) {
             Article article = (Article) sel;
+        	boolean refresh = true;
+            final Article articleSelected = controller.getArticleSelected();
+            if (articleSelected != null) {
+            	refresh = !articleSelected.equals(article);
+            }
+            if (refresh) {
             controller.setArticleSelected(article);
             controller.addArticle(article);
         }
+        }
     }
 
     public ArticleModel getModel() {
Index: src/org/openconcerto/erp/core/sales/pos/model/Article.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/model/Article.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/model/Article.java	(working copy)
@@ -18,7 +18,11 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 public class Article {
@@ -138,4 +142,18 @@
         final BigDecimal tax = new BigDecimal(TaxeCache.getCache().getTauxFromId(idTaxe)).movePointLeft(2).add(BigDecimal.ONE);
         return ttc.divide(tax, DecimalUtils.HIGH_PRECISION).setScale(6, RoundingMode.HALF_UP);
     }
+    
+    public static int getNewTempID() {
+    	final List<Article> articleList = new ArrayList<>();
+    	for (Categorie c : Categorie.getAllCategories()) {
+    		articleList.addAll(c.getArticles());
+    	}
+    	Article lastTemporaryArticle =  Collections.min(articleList, new Comparator<Article>() {
+    	    @Override
+    	    public int compare(Article o1, Article o2) {
+    	        return Integer.compare(o1.getId(), o2.getId());
+    	    }
+    	});
+    	return Math.min(lastTemporaryArticle.getId(), 0) -1;    	
+    }
 }
Index: src/org/openconcerto/erp/core/sales/pos/ui/CategorieSelector.java
===================================================================
--- src/org/openconcerto/erp/core/sales/pos/ui/CategorieSelector.java	(revision 150)
+++ src/org/openconcerto/erp/core/sales/pos/ui/CategorieSelector.java	(working copy)
@@ -159,7 +159,7 @@
                     CategorieSelector.this.previous = newCategory.getParent();
                 }
                 articleModel.setCategorie(newCategory);
-                controller.setArticleSelected(null);
+                controller.setArticleSelected(null); // Unset current article
             }
 
         });
@@ -178,7 +178,16 @@
                 this.list.clearSelection();
             }
             this.articleModel.setCategorie(c);
-            this.controller.setArticleSelected(null);
+            
+            // Clear article selection only if needed
+            final Article articleSelected = this.controller.getArticleSelected();
+            if (articleSelected != null) {
+                final Categorie selectedArticleCategory = articleSelected.getCategorie();
+                if (!(c.equals(selectedArticleCategory)))
+					this.controller.setArticleSelected(null); // Unset current article
+            } else {
+            	this.controller.setArticleSelected(null); // Unset current article
+            }
         }
     }
 
